All files / src/app/api/dev/tickets/[id]/comments route.ts

0% Statements 0/121
100% Branches 0/0
0% Functions 0/1
0% Lines 0/121

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122                                                                                                                                                                                                                                                   
export const dynamic = "force-dynamic";

/**
 * Dev Ticket Comments API
 * GET /api/dev/tickets/[id]/comments - Get all comments for a ticket
 * POST /api/dev/tickets/[id]/comments - Add a comment to a ticket
 */

import { NextRequest, NextResponse } from 'next/server';
import { Session } from "next-auth";
import {
  withAdmin,
  withErrorHandling,
  successResponse,
  createdResponse,
  ApiError,
  ApiSuccessResponse,
  ApiErrorResponse } from "@/lib/api";
import { RouteContext } from "@/lib/api/middleware";
import type { AuthenticatedUser } from '@/lib/api/middleware/types';
import { prisma } from '@/lib/prisma';
import { CreateDevCommentSchema } from '@/lib/validation/dev-ticket-schemas';
import { createTicketHistory } from '@/lib/dev-ticket';
import { logger } from '@/lib/logging';

interface RouteParams {
  params: Promise<{ id: string }>;
}

async function handleGet(
  request: NextRequest,
  context: RouteContext | undefined
): Promise<NextResponse<ApiSuccessResponse<unknown> | ApiErrorResponse>> {
  const { id } = await (context as RouteParams).params;

  // Verify ticket exists
  const ticket = await prisma.devTicket.findUnique({
    where: { id },
    select: { id: true } });

  if (!ticket) {
    throw ApiError.notFound('Ticket not found');
  }

  const comments = await prisma.devTicketComment.findMany({
    where: { ticketId: id },
    include: {
      author: {
        select: { id: true, name: true, email: true, image: true } },
      attachments: true },
    orderBy: { createdAt: 'asc' } });

  return successResponse(comments);
}

async function handlePost(
  request: NextRequest,
  context: RouteContext | undefined,
  session: Session,
  user: AuthenticatedUser
): Promise<NextResponse<ApiSuccessResponse<unknown> | ApiErrorResponse>> {
  const { id } = await (context as RouteParams).params;
  const body = await request.json();

  const validationResult = CreateDevCommentSchema.safeParse(body);
  if (!validationResult.success) {
    throw ApiError.validation(
      'Validation failed',
      validationResult.error.flatten().fieldErrors
    );
  }

  // Verify ticket exists
  const ticket = await prisma.devTicket.findUnique({
    where: { id },
    select: { id: true, ticketNumber: true } });

  if (!ticket) {
    throw ApiError.notFound('Ticket not found');
  }

  const data = validationResult.data;

  // Create the comment
  const comment = await prisma.devTicketComment.create({
    data: {
      ticketId: id,
      authorId: user.id,
      content: data.content,
      isInternal: data.isInternal },
    include: {
      author: {
        select: { id: true, name: true, email: true, image: true } },
      attachments: true } });

  // Record in history
  await createTicketHistory(
    id,
    user.id,
    'comment_added',
    undefined,
    undefined,
    comment.id
  );

  // Update ticket's updatedAt
  await prisma.devTicket.update({
    where: { id },
    data: { updatedAt: new Date() } });

  logger.info(`Added comment to ticket ${ticket.ticketNumber}`, {
    category: 'DEV_TICKETS',
    ticketId: id,
    commentId: comment.id,
    userId: user.id });

  return createdResponse(comment);
}

export const GET = withErrorHandling(withAdmin(handleGet));
export const POST = withErrorHandling(withAdmin(handlePost));